/**
 * Copyright (C) 2013 DaiKit.com - daikit-commons module (admin@daikit.com)
 *
 *         Project home : http://code.daikit.com/commons
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.daikit.commons.shared.exception.context;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.daikit.commons.shared.exception.DkException;
import com.daikit.commons.shared.miscs.Couple;


/**
 * Root Exception context
 * 
 * @author tcaselli
 * @version $Revision$ Last modifier: $Author$ Last commit: $Date$
 */
public class DkExceptionContext implements Serializable
{

	/**
	 * 
	 */
	private static final long serialVersionUID = 5311471384777216829L;

	private Map<String, String> contextMap = new HashMap<String, String>();
	private List<Couple<String, List<String>>> stackTrace = new ArrayList<Couple<String, List<String>>>();

	private static final int stackTraceMaxDepth = 10;

	/**
	 * Key for server date
	 */
	public static final String CREATION_DATE = "Creation Date";
	/**
	 * REQUEST
	 */
	public static final String REQUEST = "Request";
	/**
	 * MESSAGE
	 */
	public static final String MESSAGE = "Message";
	/**
	 * LANGUAGE_ISO_CODE
	 */
	public static final String LANGUAGE_ISO_CODE = "Language";
	/**
	 * USER_LOGGED
	 */
	public static final String USER_LOGGED = "User Logged";

	/**
	 * Constructor without message. May be added afterwards with {@link #put(String, Object)} method.
	 */
	public DkExceptionContext()
	{
	}

	/**
	 * Constructor with message. May be added afterwards with {@link #put(String, Object)} method.
	 * 
	 * @param message
	 *           the message
	 */
	public DkExceptionContext(final String message)
	{
		this();
		setMessage(message);
	}

	// -------------------------------------------------------------------------------------------
	// ------------ METHODS
	// -------------------------------------------------------------------------------------------

	/**
	 * Build the stack trace of given {@link DkException}
	 * 
	 * @param exception
	 *           a {@link DkException}
	 */
	public final void buildStackTrace(final DkException exception)
	{
		Throwable e = exception;
		for (int i = 0; i < stackTraceMaxDepth && e != null; i++)
		{
			final List<String> currentStackList = new ArrayList<String>();
			for (final StackTraceElement stackTraceElement : e.getStackTrace())
			{
				currentStackList.add(stackTraceElement.toString());
			}
			final String name = "[" + e.getClass().getName() + "]: " + e.getMessage();
			exception.getContext().getStackTrace().add(new Couple<String, List<String>>(name, currentStackList));
			e = e.getCause();
		}
	}

	/**
	 * @param key
	 *           the key for context
	 * @return the value for this context key
	 */
	public String get(final String key)
	{
		return contextMap.get(key);
	}

	/**
	 * @param key
	 *           the key for context
	 * @param value
	 *           the value to set for this context key
	 */
	public void put(final String key, final Object value)
	{
		contextMap.put(key, value == null ? null : value.toString());
	}

	protected StringBuilder getToStringBuilder()
	{
		final StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.append("(").append(getClass().getName()).append(":");
		return stringBuilder;
	}

	protected String finalizeToString(final StringBuilder stringBuilder)
	{
		return stringBuilder.append(")").toString();
	}

	@Override
	public String toString()
	{
		final StringBuilder stringBuilder = getToStringBuilder();
		if (stringBuilder.charAt(stringBuilder.length() - 1) == ':')
		{
			stringBuilder.append(contextMap.toString());
		}
		return finalizeToString(getToStringBuilder());
	}

	// -------------------------------------------------------------------------------------------
	// ------------ GETTERS / SETTERS
	// -------------------------------------------------------------------------------------------

	/**
	 * @param contextMap
	 *           the contextMap to set
	 */
	protected void setContextMap(final Map<String, String> contextMap)
	{
		this.contextMap = contextMap;
	}

	/**
	 * @return the contextMap
	 */
	public Map<String, String> getContextMap()
	{
		return contextMap;
	}

	/**
	 * @return the stackTrace
	 */
	public List<Couple<String, List<String>>> getStackTrace()
	{
		return stackTrace;
	}

	/**
	 * @param stackTrace
	 *           the stackTrace to set
	 */
	public void setStackTrace(final List<Couple<String, List<String>>> stackTrace)
	{
		this.stackTrace = stackTrace;
	}

	/**
	 * @return the serverDate
	 */
	public String getCreationDate()
	{
		return contextMap.get(CREATION_DATE);
	}

	/**
	 * @return the request
	 */
	public String getRequest()
	{
		return contextMap.get(REQUEST);
	}

	/**
	 * @return the message
	 */
	public String getMessage()
	{
		return contextMap.get(MESSAGE);
	}

	/**
	 * @param creationDate
	 *           the creation date
	 */
	public void setCreationDate(final String creationDate)
	{
		contextMap.put(CREATION_DATE, creationDate);
	}

	/**
	 * @param request
	 *           the request
	 */
	public void setRequest(final String request)
	{
		contextMap.put(REQUEST, request);
	}

	/**
	 * @param message
	 *           the message
	 */
	public void setMessage(final String message)
	{
		contextMap.put(MESSAGE, message);
	}

	/**
	 * @return the languageIsoCode
	 */
	public String getLanguageIsoCode()
	{
		return get(LANGUAGE_ISO_CODE);
	}

	/**
	 * @return the userLogged
	 */
	public String getUserLogged()
	{
		return get(USER_LOGGED);
	}

	/**
	 * @param languageIsoCode
	 *           the languageIsoCode to set
	 */
	public void setLanguageIsoCode(final String languageIsoCode)
	{
		contextMap.put(LANGUAGE_ISO_CODE, languageIsoCode);
	}

	/**
	 * @param userLogged
	 *           the userLogged to set
	 */
	public void setUserLogged(final String userLogged)
	{
		contextMap.put(USER_LOGGED, userLogged);
	}
}
